package com.frank.loadinglibrary.special.element;

import com.frank.loadinglibrary.base.baseanimations.BaseAnimationController;
import com.frank.loadinglibrary.base.baseelement.BaseElement;
import com.frank.loadinglibrary.base.baseelement.IComponentCallback;
import com.frank.loadinglibrary.utils.DensityUtil;
import com.frank.loadinglibrary.utils.SimpleAnimatorListener;
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorProperty;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.render.BlendMode;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.Path;
import ohos.agp.utils.Color;
import ohos.agp.utils.Point;
import ohos.agp.utils.RectFloat;
import ohos.agp.utils.TextTool;

import java.util.ArrayList;

/**
 * @author SUQI
 * @date 2021/6/8
 * @description 漏斗动画
 **/
public class SandClockElement extends BaseElement implements BaseAnimationController.IAnimationController<AnimatorValue> {

    private static String DEFAULT_GLASS_COLOR = "#8971fe";
    private static String DEFAULT_SAND_COLOR = "#de13e3";
    private static String DEFAULT_BG_COLOR = "#ffffff";
    private Paint mPaint;
    private String mGlassColor;
    private String mSandColor;
    private String mBackgroundColor;
    private float mCapHeight;
    private float mGlassThick;
    private float sandFraction = 0f;
    private boolean isUpright = true;

    private SandClockElement(IComponentCallback iComponentCallback) {
        callback = iComponentCallback;
    }

    @Override
    public ArrayList<AnimatorValue> onCreateAnimators() {
        ArrayList<AnimatorValue> list = new ArrayList<>();
        AnimatorValue sandAnimatorValue = new AnimatorValue();
        sandAnimatorValue.setDuration(5000);
        sandAnimatorValue.setLoopedCount(0);
        sandAnimatorValue.setValueUpdateListener((animatorValue, v) -> {
            sandFraction = v;
            callback.reflushComponent();
        });

        sandAnimatorValue.setStateChangedListener(new SimpleAnimatorListener() {
            @Override
            public void onEnd(Animator animator) {
                super.onEnd(animator);
                float degree;
                if (isUpright) {
                    degree = 180;
                } else {
                    degree = 0;
                }
                AnimatorProperty animatorProperty = callback.getAnimatorProperty();
                animatorProperty.setStateChangedListener(new SimpleAnimatorListener() {
                    @Override
                    public void onEnd(Animator animator) {
                        super.onEnd(animator);
                        isUpright = !isUpright;
                        sandAnimatorValue.start();
                    }
                });
                animatorProperty
                        .rotate(degree)
                        .setDuration(1000)
                        .start();

            }
        });
        list.add(sandAnimatorValue);
        return list;
    }

    @Override
    public void onAnimationReset() {
        //do nothing because donot need to reset animation params
    }

    @Override
    public BaseAnimationController.IAnimationController initAnimationController() {
        return this;
    }

    @Override
    public void initPaint() {
        super.initPaint();
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.FILL_STYLE);
        mPaint.setAntiAlias(true);
    }

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
        drawBackground(canvas);
        int layerId = canvas.saveLayer(new RectFloat(0, 0, getWidth(), getHeight()), mPaint);
        drawGlass(canvas, true);
        mPaint.setBlendMode(BlendMode.SRC_IN);
        drawSand(canvas);
        mPaint.setBlendMode(null);
        canvas.restoreToCount(layerId);
        drawGlass(canvas, false);
    }

    private void drawBackground(Canvas canvas) {
        mPaint.setStyle(Paint.Style.FILL_STYLE);
        mPaint.setColor(new Color(Color.getIntColor(mBackgroundColor)));
        canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
    }

    private void drawSand(Canvas canvas) {

        float sandTotalHeight = getHeight() / 2f - getCapHeight();
        mPaint.setStyle(Paint.Style.FILL_STYLE);
        mPaint.setColor(new Color(Color.getIntColor(mSandColor)));
        if (isUpright) {
            RectFloat sandRect = new RectFloat(0, getCapHeight() + sandTotalHeight * sandFraction, getWidth(), getHeight() / 2f);
            canvas.drawRect(sandRect, mPaint);

            canvas.drawRect(0, getHeight() / 2f + (1 - sandFraction) * sandTotalHeight, getWidth(), getHeight(), mPaint);
        } else {
            RectFloat sandRect = new RectFloat(0, getCapHeight(), getWidth(), sandTotalHeight * sandFraction + getCapHeight());
            canvas.drawRect(sandRect, mPaint);

            canvas.drawRect(0, getHeight() / 2f, getWidth(), getHeight() - sandFraction * sandTotalHeight - getCapHeight(), mPaint);
        }
    }

    private void drawGlass(Canvas canvas, boolean isModule) {

        int width = (int) getWidth();
        int height = (int) getHeight();
        float curveHeight = height - 2 * mCapHeight;
        int capWidth = width / 2;
        float capHeight = DensityUtil.dip2px(callback.getContext(), mCapHeight);
        float capLeft = (width - capWidth) / 2f;
        float capRight = capLeft + capWidth;
        float upperCapTop = 0;
        float upperCapBottom = capHeight;

        if (isModule) {
            mPaint.setColor(new Color(Color.getIntColor(mBackgroundColor)));
        } else {
            mPaint.setColor(new Color(Color.getIntColor(mGlassColor)));
            canvas.drawRoundRect(new RectFloat(capLeft, upperCapTop, capRight, upperCapBottom), capHeight / 2f, capHeight / 2f, mPaint);

            float lowerCapTop = height - capHeight;
            float lowerCapBottom = height;
            canvas.drawRoundRect(new RectFloat(capLeft, lowerCapTop, capRight, lowerCapBottom), capHeight / 2f, capHeight / 2f, mPaint);

            mPaint.setStyle(Paint.Style.STROKE_STYLE);
            mPaint.setStrokeWidth(mGlassThick);
        }


        float xOffset = capWidth / 5f;
        Path curvePath = new Path();
        Point leftTopPoint = new Point(capLeft + capWidth / 10f, upperCapBottom - mGlassThick);
        Point leftJoint = new Point(width / 2f - DensityUtil.dip2px(callback.getContext(), 1.5f), height / 2f);
        Point upperLeftCenterPoint = new Point((leftTopPoint.getPointX() + leftJoint.getPointX()) / 2f - xOffset, (leftTopPoint.getPointY() + leftJoint.getPointY()) / 2f);
        Point lowerLeftCenterPoint = new Point((leftTopPoint.getPointX() + leftJoint.getPointX()) / 2f - xOffset, (leftTopPoint.getPointY() + leftJoint.getPointY()) / 2f + curveHeight / 2f);
        Point leftBottomPoint = new Point(leftTopPoint.getPointX(), height - capHeight + mGlassThick);
        curvePath.moveTo(leftTopPoint);
        curvePath.quadTo(upperLeftCenterPoint, leftJoint);
        curvePath.quadTo(lowerLeftCenterPoint, leftBottomPoint);


        Point rightTopPoint = new Point(capRight - capWidth / 10f, upperCapBottom - mGlassThick);
        Point rightJoint = new Point(width / 2f + DensityUtil.dip2px(callback.getContext(), 1.5f), height / 2f);
        Point upperRightCenterPoint = new Point((rightTopPoint.getPointX() + rightJoint.getPointX()) / 2f + xOffset, (rightTopPoint.getPointY() + rightJoint.getPointY()) / 2f);
        Point lowerRightCenterPoint = new Point((rightTopPoint.getPointX() + rightJoint.getPointX()) / 2f + xOffset, (rightTopPoint.getPointY() + rightJoint.getPointY()) / 2f + curveHeight / 2f);
        Point rightBottomPoint = new Point(rightTopPoint.getPointX(), height - capHeight + mGlassThick);
        curvePath.lineTo(rightBottomPoint);
        curvePath.quadTo(lowerRightCenterPoint, rightJoint);
        curvePath.quadTo(upperRightCenterPoint, rightTopPoint);
        canvas.drawPath(curvePath, mPaint);

        if (isModule) {
            mPaint.setColor(new Color(Color.getIntColor(mSandColor)));
            if (isUpright) {
                canvas.drawRect(leftJoint.getPointX(), height / 2f, rightJoint.getPointX(), height - getCapHeight(), mPaint);
            } else {
                canvas.drawRect(leftJoint.getPointX(), capHeight, rightJoint.getPointX(), getHeight() / 2f, mPaint);

            }
        }
    }

    private float getCapHeight() {
        return DensityUtil.dip2px(callback.getContext(), mCapHeight);
    }

    private void apply(SandClockBuilder builder) {
        mGlassColor = !TextTool.isNullOrEmpty(builder.glassColor) ? builder.glassColor : DEFAULT_GLASS_COLOR;
        mSandColor = !TextTool.isNullOrEmpty(builder.sandColor) ? builder.sandColor : DEFAULT_SAND_COLOR;
        mBackgroundColor = !TextTool.isNullOrEmpty(builder.backgroundColor) ? builder.backgroundColor : DEFAULT_BG_COLOR;
        mGlassThick = builder.glassThick > 0 ? builder.glassThick : 10;
        mCapHeight = builder.capHeight > 0 ? builder.capHeight : 5;
    }

    public static class SandClockBuilder {
        private IComponentCallback iComponentCallback;
        private String glassColor;
        private String backgroundColor;
        private String sandColor;
        private float glassThick;
        private float capHeight;

        public SandClockBuilder setGlassColor(String glassColor) {
            this.glassColor = glassColor;
            return this;
        }

        public SandClockBuilder setBackgroundColor(String backgroundColor) {
            this.backgroundColor = backgroundColor;
            return this;
        }

        public SandClockBuilder setSandColor(String sandColor) {
            this.sandColor = sandColor;
            return this;
        }

        public SandClockBuilder setGlassThick(float glassThick) {
            this.glassThick = glassThick;
            return this;
        }

        public SandClockBuilder setCapHeight(float capHeight) {
            this.capHeight = capHeight;
            return this;
        }

        public SandClockBuilder(IComponentCallback iComponentCallback) {
            this.iComponentCallback = iComponentCallback;
        }

        public SandClockElement build() {
            SandClockElement sandClockElement = new SandClockElement(iComponentCallback);
            sandClockElement.apply(this);
            return sandClockElement;
        }
    }
}
